home *** CD-ROM | disk | FTP | other *** search
/ AmigActive 23 / AACD 23.iso / AACD / Online / opennap / main.c < prev    next >
C/C++ Source or Header  |  2001-06-08  |  34KB  |  1,285 lines

  1. /* Copyright (C) 2000-1 drscholl@users.sourceforge.net
  2.    This is free software distributed under the terms of the
  3.    GNU Public License.  See the file COPYING for details.
  4.  
  5.    $Id: main.c,v 1.301 2001/03/03 01:00:05 drscholl Exp $ */
  6.  
  7. /* Modified 29/05/01 : Removed default SHAREDIR, since we can't really assume
  8.       for Amiga port : a default directory on the Amiga
  9.  
  10.             30/05/01 : Added include "confdefs.h" since we are not using the
  11.                        CONFIGURE script
  12.  
  13.                        Added define for htons() and ntohs(), which are not
  14.                        defined in the Amiga GCC 2.7.0 includes
  15.  
  16.                        Added value for __auto_socket_vers to permit automatic
  17.                        opening/closing of bsdsocket.library by socket.o
  18.  
  19.                        Added include "extrasocket.h" with inlines for a few
  20.                        bsdsocket.library functions not emulated by ixemul
  21.  
  22.                        Removed call to setsid(), not supported by ixemul
  23. */
  24.  
  25. #include "confdefs.h"
  26.  
  27. #define htons(x) (x)
  28. #define ntohs(x) (x)
  29.  
  30. unsigned long __auto_socket_vers = 4;
  31.  
  32. #if defined(WIN32) && !defined(__CYGWIN__)
  33. #include <windows.h>
  34. #include <winsock.h>
  35. #endif /* WIN32 */
  36. #include <stdlib.h>
  37. #include <stdio.h>
  38. #include <errno.h>
  39. #include <time.h>
  40. #include <sys/stat.h>
  41. #include <ctype.h>
  42. #if !defined(WIN32) || defined(__CYGWIN__)
  43. #include <unistd.h>
  44. #include <sys/socket.h>
  45. #include <netinet/in.h>
  46. #include <arpa/inet.h>
  47. #include <string.h>
  48. #include <sys/time.h>
  49. #endif /* !WIN32 */
  50. #include "opennap.h"
  51. #include "debug.h"
  52.  
  53. #include "extrasocket.h"
  54.  
  55. #if DEBUG
  56. #define dprint0(a)      printf(a);
  57. #define dprint1(a,b) printf(a,b);
  58. #else
  59. #define dprint0(a)
  60. #define dprint1(a,b)
  61. #endif
  62.  
  63. /* offset into global.poll[] for the given file descriptor */
  64. #define POFF(fd)        global.fdmap[fd]
  65.  
  66. /*
  67.  * Global Variables
  68.  */
  69.  
  70. global_t global;
  71.  
  72. LIST   *Bans = 0;               /* bans on ip addresses / users */
  73. LIST   *UserClass = 0;
  74.  
  75. char    Buf[2048];              /* global scratch buffer */
  76.  
  77. HASH   *Channel_Db;
  78. int     Client_Queue_Length;
  79. HASH   *Channels = 0;           /* global channel list */
  80. HASH   *Client_Versions;
  81. HASH   *Clones;
  82. int     Compression_Level = 0;
  83. char   *Config_Dir = /*SHAREDIR*/ "";
  84. u_int   Connection_Count = 0;
  85. LIST   *Destroy = 0;
  86. int     Flood_Commands;
  87. int     Flood_Time;
  88. LIST   *Flooders = 0;
  89. HASH   *Hotlist;                /* global hotlist */
  90. u_int   Interface = INADDR_ANY;
  91. char   *Listen_Addr = 0;
  92. int     Login_Interval;
  93. int     Login_Timeout;
  94. int     Max_Browse_Result;
  95. int     Max_Client_String;
  96. int     Max_Clones;
  97. int     Max_Command_Length;
  98. int     Max_Connections;
  99. int     Max_Hotlist;
  100. int     Max_Ignore;
  101. int     Max_Reason;
  102. int     Max_Search_Results;
  103. int     Max_Searches;
  104. int     Max_Time_Delta;
  105. int     Max_Topic;
  106. int     Max_User_Channels;      /* default, can be changed in config */
  107. LIST   *Mods = 0;               /* local mods+ */
  108. int     Nick_Expire;
  109. double  Num_Gigs = 0;           /* in kB */
  110. int     Num_Files = 0;
  111. int     Ping_Interval;
  112. int     Register_Interval;
  113. int     Search_Timeout;
  114. char   *Server_Alias = 0;
  115. LIST   *Server_Auth = 0;
  116. int     Server_Chunk = 0;
  117. u_int   Server_Ip = 0;
  118. u_int   Server_Flags = 0;
  119. char   *Server_Name = 0;
  120. LIST   *Server_Ports = 0;       /* which port(s) to listen on for connections */
  121. int     Server_Queue_Length;
  122. int     SigCaught = 0;
  123. time_t  Server_Start;           /* time at which the server was started */
  124. int     User_Db_Interval;       /* how often to save the user database */
  125. HASH   *Users;                  /* global users list */
  126. int     Warn_Time_Delta;
  127. HASH   *Who_Was;
  128. int     Who_Was_Time = 0;
  129.  
  130. #ifndef ROUTING_ONLY
  131. int     File_Count_Threshold;
  132. HASH   *File_Table;             /* global file list */
  133. int     Local_Files = 0;        /* number of files shared by local users */
  134. int     Max_Shared;
  135.  
  136. #if RESUME
  137. HASH   *MD5;                    /* global hash list */
  138. #endif /* RESUME */
  139. int     Stats_Port;             /* port to listen on for stats info */
  140. #endif /* ! ROUTING_ONLY */
  141.  
  142. #ifndef WIN32
  143. int     Uid;
  144. int     Gid;
  145. int     Connection_Hard_Limit;
  146. int     Max_Data_Size;
  147. int     Max_Rss_Size;
  148. #endif
  149. int     Max_Nick_Length;
  150. int     Max_Channel_Length = 0;
  151.  
  152. /* local server list.  NOTE that this contains pointers into the global.clients
  153.    list to speed up server-server message passing */
  154. LIST   *Servers = 0;
  155.  
  156. /* list of all servers in the cluster */
  157. LIST   *Server_Links = 0;
  158.  
  159. /* Cache of server names for caching the user->server pointers */
  160. LIST   *Server_Names = 0;
  161.  
  162. #define BACKLOG 50
  163.  
  164. void
  165. set_write (int fd)
  166. {
  167. #if HAVE_POLL
  168.     global. poll[POFF (fd)].events |= POLLOUT;
  169. #else
  170.     FD_SET (fd, &global.write_fds);
  171. #endif
  172. }
  173.  
  174. void
  175. clear_write (int fd)
  176. {
  177. #if HAVE_POLL
  178.     global. poll[POFF (fd)].events &= ~POLLOUT;
  179. #else
  180.     FD_CLR (fd, &global.write_fds);
  181. #endif
  182. }
  183.  
  184. void
  185. set_read (int fd)
  186. {
  187. #if HAVE_POLL
  188.     global. poll[POFF (fd)].events |= POLLIN;
  189. #else
  190.     FD_SET (fd, &global.read_fds);
  191. #endif
  192. }
  193.  
  194. void
  195. clear_read (int fd)
  196. {
  197. #if HAVE_POLL
  198.     global. poll[POFF (fd)].events &= ~POLLIN;
  199. #else
  200.     FD_CLR (fd, &global.read_fds);
  201. #endif
  202. }
  203.  
  204. #define CLICK 64
  205.  
  206. void
  207. add_fd (int fd)
  208. {
  209. #if HAVE_POLL
  210.     int     off;
  211.  
  212.     if (global.poll_max == global.poll_num)
  213.     {
  214.         global. poll_max += CLICK;
  215.         global. poll = REALLOC (global.poll,
  216.                                 sizeof (struct pollfd) * global.poll_max);
  217.  
  218.         for (off = global.poll_num; off < global.poll_max; off++)
  219.         {
  220.             global. poll[off].fd = -1;
  221.             global. poll[off].events = 0;
  222.             global. poll[off].revents = 0;
  223.         }
  224.     }
  225. #endif
  226.  
  227.     /* keep track of the biggest fd we've seen */
  228.     if (fd > global.max_fd)
  229.     {
  230. #if HAVE_POLL
  231.         global. fdmap = REALLOC (global.fdmap,
  232.                                  sizeof (int) * (fd + 1));
  233.  
  234.         for (off = global.max_fd + 1; off < fd + 1; off++)
  235.             global. fdmap[off] = -1;
  236. #endif
  237.         global. max_fd = fd;
  238.     }
  239.  
  240. #if HAVE_POLL
  241.     off = global.fdmap[fd] = global.poll_num++;
  242.  
  243.     global. poll[off].fd = fd;
  244.     global. poll[off].events = 0;
  245.     global. poll[off].revents = 0;
  246. #endif
  247. }
  248.  
  249. #if HAVE_POLL
  250. void
  251. remove_fd (int fd)
  252. {
  253.     if (fd == -1)
  254.     {
  255.         ASSERT (0);
  256.         return;
  257.     }
  258.  
  259.     if (global.fdmap[fd] == -1)
  260.     {
  261.         ASSERT (0);
  262.         return;
  263.     }
  264.  
  265.     if (global.fdmap[fd] < global.poll_num - 1)
  266.     {
  267.         /* swap with the last client */
  268.         int     i = global.poll[global.poll_num - 1].fd;
  269.  
  270.         ASSERT (i != -1);
  271.         ASSERT (global.poll[POFF (fd)].fd == fd);
  272.         ASSERT (global.poll[POFF (i)].fd == i);
  273.  
  274.         memcpy (&global.poll[POFF (fd)], &global.poll[POFF (i)],
  275.                 sizeof (struct pollfd));
  276.         global. fdmap[i] = POFF (fd);
  277.     }
  278.  
  279.     /* mark as unused */
  280.     global. fdmap[fd] = -1;
  281.     global. poll_num--;
  282.  
  283.     /* reset the pollfd struct */
  284.     global. poll[global.poll_num].fd = -1;
  285.     global. poll[global.poll_num].events = 0;
  286.     global. poll[global.poll_num].revents = 0;
  287. }
  288. #endif /* HAVE_POLL */
  289.  
  290. int
  291. add_client (CONNECTION * con, int is_server)
  292. {
  293.     /* allocate more space if required */
  294.     if (global.clients_max == global.clients_num)
  295.     {
  296.         global. clients_max += CLICK;
  297.         global. clients = REALLOC (global.clients,
  298.                                    sizeof (CONNECTION *) *
  299.                                    global.clients_max);
  300.     }
  301.     con->id = global.clients_num++;
  302.     global. clients[con->id] = con;
  303.  
  304.     add_fd (con->fd);
  305.  
  306.     con->class = CLASS_UNKNOWN;
  307.     con->timer = global.current_time;   /* set a login timer */
  308.  
  309.     set_nonblocking (con->fd);
  310.     set_keepalive (con->fd, 1); /* enable tcp keepalive messages */
  311.  
  312.     if (is_server)
  313.     {
  314.         /* we are doing a non-blocking connect, wait for the socket to become
  315.          * writable
  316.          */
  317.         con->connecting = 1;
  318.         set_write (con->fd);
  319.     }
  320.     else
  321.     {
  322.         /* user connection, wait for some input */
  323.         set_read (con->fd);
  324.     }
  325.     return 0;
  326. }
  327.  
  328. void
  329. send_all_clients (int tag, const char *fmt, ...)
  330. {
  331.     va_list ap;
  332.     int     len;
  333.     int     i;
  334.  
  335.     va_start (ap, fmt);
  336.     vsnprintf (Buf + 4, sizeof (Buf) - 4, fmt, ap);
  337.     va_end (ap);
  338.     len = strlen (Buf + 4);
  339.     set_tag (Buf, tag);
  340.     set_len (Buf, len);
  341.     len += 4;
  342.     for (i = 0; i < global.clients_num; i++)
  343.         if (ISUSER (global.clients[i]))
  344.                     queue_data (global.clients[i], Buf, len);
  345. }
  346.  
  347. #ifndef ROUTING_ONLY
  348.  
  349. static void
  350. report_stats (int fd)
  351. {
  352.     int     n;
  353.     struct sockaddr_in sin;
  354.     socklen_t sinsize = sizeof (sin);
  355.     float   loadavg = 0;
  356.  
  357.     n = accept (fd, (struct sockaddr *) &sin, &sinsize);
  358.     if (n == -1)
  359.     {
  360.         nlogerr ("report_stats", "accept");
  361.         return;
  362.     }
  363.     log ("report_stats: connection from %s:%d", inet_ntoa (sin.sin_addr),
  364.          htons (sin.sin_port));
  365. #ifdef linux
  366.     {
  367.         FILE   *f = fopen ("/proc/loadavg", "r");
  368.  
  369.         if (f)
  370.         {
  371.             fscanf (f, "%f", &loadavg);
  372.             fclose (f);
  373.         }
  374.         else
  375.         {
  376.             log ("report_stats: /proc/loadavg: %s (errno %d)",
  377.                  strerror (errno), errno);
  378.         }
  379.     }
  380. #endif /* linux */
  381.     snprintf (Buf, sizeof (Buf), "%d %d %.2f %.0f 0\n", Users->dbsize,
  382.               Num_Files, loadavg, Num_Gigs * 1024.);
  383.     WRITE (n, Buf, strlen (Buf));
  384.     CLOSE (n);
  385. }
  386. #endif /* !ROUTING_ONLY */
  387.  
  388. static void
  389. update_stats (void)
  390. {
  391.     int     numServers = list_count (Servers);
  392.     time_t  delta;
  393.  
  394.     delta = global.current_time - global.last_click;
  395.  
  396.     strcpy (Buf, ctime (&Server_Start));
  397.     Buf[strlen (Buf) - 1] = 0;
  398.     log ("update_stats: server was started on %s", Buf);
  399.     strcpy (Buf, ctime (&global.current_time));
  400.  
  401.     Buf[strlen (Buf) - 1] = 0;
  402.     log ("update_stats: current time is %s", Buf);
  403.     log ("update_stats: library is %d GB, %d files, %d users",
  404.          (int) (Num_Gigs / 1048576.), Num_Files, Users->dbsize);
  405.     log ("update_stats: %d local clients, %d linked servers",
  406.          global.clients_num - numServers, numServers);
  407.  
  408. #ifndef ROUTING_ONLY
  409.     log ("update_stats: %d local files", Local_Files);
  410.     log ("update_stats: File_Table contains %d entries", File_Table->dbsize);
  411. #endif
  412.     if (delta > 0)
  413.         log ("update_stats: %d searches/sec", global.search_count / delta);
  414.  
  415.     log ("update_stats: User_Db contains %d entries", User_Db->dbsize);
  416.     log ("update_stats: %d channels", Channels->dbsize);
  417.     if (delta > 0)
  418.         log ("update_stats: %d kbytes/sec in, %d kbytes/sec out",
  419.              (int) (global.bytes_in / 1024 / delta),
  420.              (int) (global.bytes_out / delta / 1024));
  421.     global. total_bytes_in += global.bytes_in;
  422.     global. total_bytes_out += global.bytes_out;
  423.     log ("update_stats: %.0f bytes sent, %.0f bytes received",
  424.          global.total_bytes_out, global.total_bytes_in);
  425.  
  426.     /* reset counters */
  427.     global. bytes_in = 0;
  428.     global. bytes_out = 0;
  429.     global. search_count = 0;
  430.     global. last_click = global.current_time;
  431.  
  432.     /* since we send the same data to many people, optimize by forming
  433.        the message once then writing it out */
  434.     send_all_clients (MSG_SERVER_STATS, "%d %d %d", Users->dbsize,
  435.                       Num_Files, (int) (Num_Gigs / 1048576.));
  436.  
  437. #ifndef ROUTING_ONLY
  438.     /* send live stats to stat server */
  439.     stat_server_push ();
  440. #endif
  441. }
  442.  
  443. /* accept all pending connections */
  444. static void
  445. accept_connection (int s)
  446. {
  447.     CONNECTION *cli = 0;
  448.     socklen_t sinsize;
  449.     struct sockaddr_in sin;
  450.     int     f;
  451.  
  452.     for (;;)
  453.     {
  454.         sinsize = sizeof (sin);
  455.         /* set an alarm just in case we end up blocking when a client
  456.          * disconnects before we get to the accept()
  457.          */
  458.         alarm (3);
  459.         if ((f = accept (s, (struct sockaddr *) &sin, &sinsize)) < 0)
  460.         {
  461.             alarm (0);
  462.             if (N_ERRNO != EWOULDBLOCK)
  463.                 nlogerr ("accept_connection", "accept");
  464.             return;
  465.         }
  466.         alarm (0);
  467.  
  468.         if (!acl_connection_allowed (BSWAP32 (sin.sin_addr.s_addr)))
  469.         {
  470.             log ("accept_connection: connection from %s denied by ACLs",
  471.                  inet_ntoa (sin.sin_addr));
  472.             CLOSE (f);
  473.             continue;
  474.         }
  475.  
  476.         if ((cli = new_connection ()) == 0)
  477.             goto error;
  478.         cli->fd = -1;
  479.  
  480.         /* if we have a local connection, use the external
  481.            interface so others can download from them */
  482.         if (sin.sin_addr.s_addr == inet_addr ("127.0.0.1"))
  483.         {
  484.             cli->ip = BSWAP32 (Server_Ip);
  485.             cli->host = STRDUP (Server_Name);
  486.         }
  487.         else
  488.         {
  489.             cli->ip = BSWAP32 (sin.sin_addr.s_addr);
  490.             cli->host = STRDUP (inet_ntoa (sin.sin_addr));
  491.         }
  492.         if (!cli->host)
  493.         {
  494.             OUTOFMEMORY ("accept_connection");
  495.             goto error;
  496.         }
  497.  
  498.         cli->port = ntohs (sin.sin_port);
  499.         cli->fd = f;
  500.  
  501.         if (add_client (cli, 0 /* not a server */ ))
  502.             goto error;
  503.     }
  504.  
  505.     /* not reached */
  506.     ASSERT (0);
  507.     return;
  508.   error:
  509.     if (cli)
  510.     {
  511.         if (cli->fd != -1)
  512.             CLOSE (cli->fd);
  513.         if (cli->host)
  514.             FREE (cli->host);
  515.         FREE (cli);
  516.     }
  517.     else
  518.         CLOSE (f);
  519. }
  520.  
  521. static void
  522. usage (void)
  523. {
  524.     fprintf (stderr,
  525.              "usage: %s [ -bhsv ] [ -c DIR ] [ -p PORT ] [ -l IP ]\n",
  526.              PACKAGE);
  527.     fprintf (stderr, "  -c DIR  read config files from DIR (default: %s)\n",
  528.              /*SHAREDIR*/"");
  529.     fputs ("  -b                run as a background process (daemon)\n",
  530.            stderr);
  531.     fputs ("  -h                print this help message\n", stderr);
  532.     fputs
  533.         ("  -l IP               listen only on IP instead of all interfaces\n",
  534.          stderr);
  535.     fputs ("  -p PORT   listen on PORT for connections (default: 8888)\n",
  536.            stderr);
  537.     fputs
  538.         ("  -s          channels may only be created by privileged users\n",
  539.          stderr);
  540.     fputs ("  -v                display version information\n", stderr);
  541.     exit (0);
  542. }
  543.  
  544. static void
  545. version (void)
  546. {
  547.     fprintf (stderr, "%s %s\n", PACKAGE, VERSION);
  548.     fprintf (stderr, "Copyright (C) 2000 drscholl@users.sourceforge.net\n");
  549.     exit (0);
  550. }
  551.  
  552. #ifdef __CYGWIN__
  553. extern char *optarg;
  554. #endif
  555.  
  556. static int *
  557. args (int argc, char **argv, int *sockfdcount)
  558. {
  559.     int     i;
  560.     LIST   *ports = 0, *tmpList;
  561.     int     iface = -1;
  562.     int    *sockfd;
  563.     int     not_root = 1;
  564.     int     port;
  565.  
  566. #ifndef WIN32
  567.     not_root = (getuid () != 0);
  568. #endif
  569.  
  570.     while ((i = getopt (argc, argv, "bc:hl:p:svD")) != -1)
  571.     {
  572.         switch (i)
  573.         {
  574.         case 'b':
  575.             Server_Flags |= ON_BACKGROUND;
  576.             break;
  577.         case 'D':
  578.             Server_Flags |= ON_NO_LISTEN;       /* dont listen on stats port */
  579.             break;
  580.         case 'c':
  581.             /* ignore the command line option if we're running as root.
  582.              * we don't allow non-root users to specify their own config
  583.              * files to avoid possible security problems.
  584.              */
  585.             if (not_root)
  586.                 Config_Dir = optarg;
  587.             else
  588.             {
  589.                 log ("args: can't use -c when run as root");
  590.                 exit (1);
  591.             }
  592.             break;
  593.         case 'l':
  594.             iface = inet_addr (optarg);
  595.             break;
  596.         case 'p':
  597.             /* don't allow a privileged port to be used from the command line
  598.              * if running as root.  this can only be specified in the
  599.              * configuration file defined at compile time.
  600.              */
  601.             port = atoi (optarg);
  602.             if (not_root || port > 1024)
  603.             {
  604.                 tmpList = CALLOC (1, sizeof (LIST));
  605.                 tmpList->data = STRDUP (optarg);
  606.                 tmpList->next = ports;
  607.                 ports = tmpList;
  608.             }
  609.             else
  610.             {
  611.                 log ("args: privileged ports not allowed on command line");
  612.                 exit (1);
  613.             }
  614.             break;
  615.         case 's':
  616.             Server_Flags |= ON_STRICT_CHANNELS;
  617.             break;
  618.         case 'v':
  619.             version ();
  620.             break;
  621.         default:
  622.             usage ();
  623.         }
  624.     }
  625.  
  626. #if USE_CHROOT
  627.     /* we always use the compiled directory instead of the one on the command
  628.      * line here to avoid problems.
  629.      */
  630.     if (chroot (/*SHAREDIR*/""))
  631.     {
  632.         perror ("chroot");
  633.         exit (1);
  634.     }
  635.     if (chdir ("/"))
  636.     {
  637.         perror ("chdir");
  638.         exit (1);
  639.     }
  640.     /* force the config files to be relative to the chroot jail */
  641.     Config_Dir = "/";
  642.     /* privs will be dropped later.  we still need them to read the the
  643.      * config file and set resources.
  644.      */
  645. #endif
  646.  
  647. #if !defined(WIN32) && !defined(__EMX__)
  648.     /* check whether to run in the background */
  649.     if (Server_Flags & ON_BACKGROUND)
  650.     {
  651.         if (fork () == 0)
  652.             /*setsid ()*/;
  653.         else
  654.             exit (0);
  655.     }
  656. #endif
  657.  
  658.     if (init_server ())
  659.         exit (1);
  660.  
  661.     /* if the interface was specified on the command line, override the
  662.      * value from the config file
  663.      */
  664.     if (iface != -1)
  665.     {
  666.         Interface = iface;
  667.         Server_Ip = iface;
  668.     }
  669.  
  670.     /* if port(s) were specified on the command line, override the values
  671.        specified in the config file */
  672.     if (!ports)
  673.         ports = Server_Ports;
  674.  
  675.     /* create the incoming connections socket(s) */
  676.     *sockfdcount = list_count (ports);
  677.     /* ensure at least one valid port */
  678.     if (*sockfdcount < 1)
  679.     {
  680.         log ("args: no server ports defined");
  681.         exit (1);
  682.     }
  683.     sockfd = CALLOC (*sockfdcount, sizeof (int));
  684.  
  685.     log ("args: listening on %d sockets", *sockfdcount);
  686.     for (i = 0, tmpList = ports; i < *sockfdcount;
  687.          i++, tmpList = tmpList->next)
  688.     {
  689.         if ((sockfd[i] = new_tcp_socket (ON_NONBLOCKING | ON_REUSEADDR)) < 0)
  690.             exit (1);
  691.         if (bind_interface (sockfd[i], Interface, atoi (tmpList->data)) == -1)
  692.             exit (1);
  693.         if (listen (sockfd[i], BACKLOG) < 0)
  694.         {
  695.             nlogerr ("args", "listen");
  696.             exit (1);
  697.         }
  698.         log ("args: listening on %s port %d", my_ntoa (Interface),
  699.              atoi (tmpList->data));
  700.         if (sockfd[i] > global.max_fd)
  701.             global. max_fd = sockfd[i];
  702.     }
  703.     if (ports != Server_Ports)
  704.         list_free (ports, free_pointer);
  705.     return sockfd;
  706. }
  707.  
  708. /* sync in-memory state to disk so we can restore properly */
  709. static void
  710. dump_state (void)
  711. {
  712.     userdb_dump ();             /* write out the user database */
  713. #ifndef ROUTING_ONLY
  714.     save_bans ();               /* write out server bans */
  715.     dump_channels ();           /* write out persistent channels file */
  716.     acl_save ();                /* save acls */
  717. #endif
  718. }
  719.  
  720. #ifndef ROUTING_ONLY
  721. static int
  722. init_stats_port (void)
  723. {
  724.     int     sp = -1;
  725.  
  726.     if (!option (ON_NO_LISTEN) && Stats_Port != -1)
  727.     {
  728.         /* listen on port 8889 for stats reporting */
  729.         if ((sp = new_tcp_socket (ON_REUSEADDR)) == -1)
  730.             exit (1);
  731.         if (bind_interface (sp, Interface, Stats_Port))
  732.             exit (1);
  733.         if (listen (sp, BACKLOG))
  734.         {
  735.             logerr ("main", "listen");
  736.             exit (1);
  737.         }
  738.         if (sp > global.max_fd)
  739.             global. max_fd = sp;
  740.     }
  741.     return sp;
  742. }
  743. #endif
  744.  
  745. int     num_reaped = 0;
  746.  
  747. /* puts the specified connection on the destroy list to be reaped at the
  748.  * end of the main event loop
  749.  */
  750. void
  751. destroy_connection (CONNECTION * con)
  752. {
  753.     LIST   *list;
  754.  
  755.     ASSERT (validate_connection (con));
  756.  
  757.     /* already destroyed */
  758.     if (con->fd == -1)
  759.         return;
  760.  
  761.     dprint1 ("destroy_connection: destroying fd %d\n", con->fd);
  762.  
  763.     if (con->destroy)
  764.     {
  765.         list = list_find (Destroy, con);
  766.         if (list)
  767.             return;             /* already destroyed */
  768.         log ("destroy_connection: error, destroyed connection not on Destroy list");
  769.         log ("destroy_connection: con->host = %s", con->host);
  770.         if (ISUSER (con))
  771.             log ("destroy_connection: con->user->nick = %s", con->user->nick);
  772.     }
  773.     else
  774.         num_reaped++;
  775.  
  776.     /* append to the list of connections to destroy */
  777.     list = CALLOC (1, sizeof (LIST));
  778.     if (!list)
  779.     {
  780.         OUTOFMEMORY ("destroy_connection");
  781.         return;
  782.     }
  783.     list->data = con;
  784.     ASSERT (list_validate (Destroy));
  785.     Destroy = list_push (Destroy, list);
  786.     ASSERT (Destroy->data == con);
  787.     con->destroy = 1;
  788.  
  789.     /* we don't want to read/write anything furthur to this fd */
  790. #if HAVE_POLL
  791.     remove_fd (con->fd);
  792. #else
  793.     FD_CLR (con->fd, &global.read_fds);
  794.     FD_CLR (con->fd, &global.write_fds);
  795. #endif /* HAVE_POLL */
  796.  
  797.     ASSERT (list_count (Destroy) == num_reaped);
  798. }
  799.  
  800. static void
  801. reap_dead_connection (CONNECTION * con)
  802. {
  803. #if DEBUG
  804.     int     i;
  805. #endif
  806.     ASSERT (validate_connection (con));
  807.  
  808. #if HAVE_POLL
  809.     ASSERT (global.fdmap[con->fd] == -1);
  810.  
  811. #if DEBUG
  812.     /* be certain the fd isn't being polled */
  813.     for (i = 0; i < global.poll_num; i++)
  814.         ASSERT (global.poll[i].fd != con->fd);
  815. #endif /* DEBUG */
  816.  
  817. #else
  818.     /* this should have already happened, but to it here just to be safe */
  819.     FD_CLR (con->fd, &global.read_fds);
  820.     FD_CLR (con->fd, &global.write_fds);
  821. #endif
  822.  
  823.     if (con->id < global.clients_num - 1)
  824.     {
  825.         /* swap this place with the last connection in the array */
  826.         global. clients[con->id] = global.clients[global.clients_num - 1];
  827.         global. clients[con->id]->id = con->id;
  828.     }
  829.     global.clients_num--;
  830.     global.clients[global.clients_num] = 0;
  831.  
  832.     /* close either the current descriptor */
  833.     CLOSE (con->fd);
  834.  
  835.     /* mark that the descriptor has been closed */
  836.     con->fd = -1;
  837.  
  838.     /* this call actually free's the memory associated with the connection */
  839.     remove_connection (con);
  840. }
  841.  
  842. /* we can't use list_free(Destroy, reap_dead_connection) here because
  843.  * reap_dead_connection might try to access `Destroy', which will be pointed
  844.  * to free'd memory.  so this function updates `Destroy' in an atomic
  845.  * fashion such that if `Destroy' is updated, we have the correct new value.
  846.  */
  847. static void
  848. reap_connections (void)
  849. {
  850.     LIST   *tmp;
  851.  
  852.     while (Destroy)
  853.     {
  854.         tmp = Destroy;
  855.         Destroy = Destroy->next;
  856.         num_reaped--;
  857.         reap_dead_connection (tmp->data);
  858.         FREE (tmp);
  859.     }
  860.     ASSERT (num_reaped == 0);
  861. }
  862.  
  863. static void
  864. flood_expire (void)
  865. {
  866.     LIST  **list, *tmp;
  867.     CONNECTION *con;
  868.  
  869.     for (list = &Flooders; *list;)
  870.     {
  871.         con = (*list)->data;
  872.         if (con->flood_start + Flood_Time < global.current_time)
  873.         {
  874.             /* flood timer expired, resume reading commands */
  875.             set_read (con->fd);
  876.             tmp = *list;
  877.             *list = (*list)->next;
  878.             FREE (tmp);
  879.         }
  880.         else
  881.             list = &(*list)->next;
  882.     }
  883. }
  884.  
  885. /* since server->server data is always queued up so it can be compressed
  886.  * in one shot, we have to explicitly call send_queued_data() for each
  887.  * server here.
  888.  */
  889. static void
  890. flush_server_data (CONNECTION * con, void *unused)
  891. {
  892.     (void) unused;
  893.     ASSERT (validate_connection (con));
  894.     if (send_queued_data (con) == -1)
  895.         destroy_connection (con);
  896. }
  897.  
  898. #if HAVE_POLL
  899. #define TIMEOUT timeout
  900. #define READABLE(c)     (global.poll[global.fdmap[c]].revents & POLLIN)
  901. #define WRITABLE(c)     (global.poll[global.fdmap[c]].revents & POLLOUT)
  902. #else
  903. #define TIMEOUT to.tv_sec
  904. #define READABLE(c)     FD_ISSET(c,&read_fds)
  905. #define WRITABLE(c)     FD_ISSET(c,&write_fds)
  906. #endif
  907.  
  908. static void
  909. server_input (CONNECTION * con, void *arg)
  910. {
  911. #if HAVE_POLL
  912.     (void) arg;
  913.     ASSERT (global.fdmap[con->fd] != -1);
  914.  
  915.     ASSERT ((global.poll[POFF (con->fd)].events & POLLIN) !=0);
  916.     if (global.poll[POFF (con->fd)].revents & POLLIN)
  917.         handle_connection (con);
  918. #else
  919.     fd_set *read_fds = (fd_set *) arg;
  920.  
  921.     if (FD_ISSET (con->fd, read_fds))
  922.         handle_connection (con);
  923. #endif
  924. }
  925.  
  926. int
  927. main (int argc, char **argv)
  928. {
  929.     int    *sockfd;             /* server sockets */
  930.     int     sockfdcount;        /* number of server sockets */
  931.     int     i;                  /* generic counter */
  932.     int     numfds;
  933.  
  934. #ifndef ROUTING_ONLY
  935.     int     sp;
  936. #endif
  937. #if HAVE_POLL
  938.     int     timeout;
  939. #else
  940.     struct timeval to;
  941.     fd_set  read_fds, write_fds;
  942. #endif
  943.  
  944. #if defined(WIN32) && !defined(__CYGWIN__)
  945.     WSADATA wsa;
  946.  
  947.     WSAStartup (MAKEWORD (1, 1), &wsa);
  948. #endif /* !WIN32 */
  949.  
  950.     memset (&global, 0, sizeof (global_t));
  951.  
  952.     /* minimize the stack space for the main loop by moving the command line
  953.        parsing code to a separate routine */
  954.     sockfd = args (argc, argv, &sockfdcount);
  955.  
  956. #ifndef ROUTING_ONLY
  957.     sp = init_stats_port ();
  958. #endif
  959.  
  960. #if HAVE_POLL
  961.     global. poll_max = global.max_fd + 1;
  962.     global. poll = CALLOC (global.poll_max, sizeof (struct pollfd));
  963.     for (i = 0; i < global.poll_max; i++)
  964.         global.poll[i].fd = -1;
  965.     global. fdmap =
  966.         CALLOC (global.poll_max, sizeof (int) * (global.max_fd + 1));
  967.     memset (global.fdmap, -1, sizeof (int) * (global.max_fd + 1));
  968. #endif
  969.  
  970.     for (i = 0; i < sockfdcount; i++)
  971.     {
  972. #if HAVE_POLL
  973.         struct pollfd *p;
  974.  
  975.         global. fdmap[sockfd[i]] = global.poll_num++;
  976.         p = &global.poll[global.fdmap[sockfd[i]]];
  977.  
  978.         p->fd = sockfd[i];
  979.         p->events = POLLIN;
  980. #else
  981.         FD_SET (sockfd[i], &global.read_fds);
  982. #endif
  983.     }
  984.  
  985. #ifndef ROUTING_ONLY
  986.     if (sp != -1)
  987.     {
  988. #if HAVE_POLL
  989.         global. fdmap[sp] = global.poll_num++;
  990.         global. poll[POFF (sp)].fd = sp;
  991.         global. poll[POFF (sp)].events = POLLIN;
  992. #else
  993.         FD_SET (sp, &global.read_fds);
  994. #endif
  995.     }
  996. #endif
  997.  
  998.     /* schedule periodic events */
  999.     add_timer (global.stat_click, -1, (timer_cb_t) update_stats, 0);
  1000.  
  1001.     add_timer (User_Db_Interval, -1, (timer_cb_t) dump_state, 0);
  1002.     add_timer (60, -1, (timer_cb_t) expire_bans, 0);
  1003.     add_timer (Ping_Interval, -1, (timer_cb_t) lag_detect, 0);
  1004.     add_timer (Who_Was_Time, -1, (timer_cb_t) expire_whowas, 0);
  1005.  
  1006.     /* initialize so we get the correct delta for the first call to
  1007.        update_stats() */
  1008.     global. last_click = global.current_time;
  1009.  
  1010.     /* auto connect remote servers if requested */
  1011.     if (option (ON_AUTO_LINK))
  1012.         auto_link ();
  1013.  
  1014.     /* main event loop */
  1015.     while (!SigCaught)
  1016.     {
  1017.         global. current_time = time (0);
  1018.  
  1019.         TIMEOUT = next_timer ();
  1020.         /* if we have a flood list and the timeout is greater than when
  1021.          * the flood expires, reset the timeout
  1022.          */
  1023.         if (Flooders && TIMEOUT > Flood_Time)
  1024.             TIMEOUT = Flood_Time;
  1025.  
  1026. #if HAVE_POLL
  1027.  
  1028. #if DEBUG
  1029.         /* check to make sure the poll[] array looks kosher */
  1030.         for (i = 0; i < global.poll_num; i++)
  1031.         {
  1032.             ASSERT (global.poll[i].fd != -1);
  1033.             ASSERT (global.fdmap[global.poll[i].fd] == i);
  1034.         }
  1035.         for (i = global.poll_num; i < global.poll_max; i++)
  1036.         {
  1037.             ASSERT (global.poll[i].fd == -1);
  1038.             ASSERT (global.poll[i].events == 0);
  1039.             ASSERT (global.poll[i].revents == 0);
  1040.         }
  1041. #endif /* DEBUG */
  1042.  
  1043.         numfds = poll (global.poll, global.poll_num, timeout * 1000);
  1044.  
  1045.         if (numfds == -1)
  1046.         {
  1047.             nlogerr ("main", "poll");
  1048.             continue;
  1049.         }
  1050. #else
  1051.         read_fds = global.read_fds;
  1052.         write_fds = global.write_fds;
  1053.  
  1054.         to.tv_usec = 0;
  1055.         numfds = select (global.max_fd + 1, &read_fds, &write_fds, 0, &to);
  1056.  
  1057.         if (numfds == -1)
  1058.         {
  1059.             nlogerr ("main", "select");
  1060.             continue;
  1061.         }
  1062. #endif
  1063.  
  1064.         /* pre-read server links */
  1065.         list_foreach (Servers, (list_callback_t) server_input,
  1066. #ifndef HAVE_POLL
  1067.                       &read_fds
  1068. #else
  1069.                       NULL
  1070. #endif
  1071.             );
  1072.  
  1073.         /* do client i/o */
  1074.         for (i = 0; i < global.clients_num; i++)
  1075.         {
  1076. #if HAVE_POLL
  1077.             int     off = POFF (global.clients[i]->fd);
  1078.  
  1079.             if (global.poll[off].revents & (POLLNVAL | POLLHUP | POLLERR))
  1080.             {
  1081.                 if (global.poll[off].revents & POLLERR)
  1082.                 {
  1083.                     int     err;
  1084.                     socklen_t errlen = sizeof (err);
  1085.  
  1086.                     /* error */
  1087.                     if (getsockopt (global.poll[off].fd, SOL_SOCKET,
  1088.                                     SO_ERROR, &err, &errlen))
  1089.                                 logerr ("main", "getsockopt");
  1090.  
  1091.                     else
  1092.                     {
  1093.                         log ("main: fd %d (%s): %s (errno %d)",
  1094.                              global.poll[off].fd,
  1095.                              global.clients[i]->host, strerror (err), err);
  1096.                     }
  1097.                 }
  1098.                 else
  1099.                     log ("main: fd %d %s", global.poll[off].fd,
  1100.                          (global.poll[off].
  1101.                           revents & POLLNVAL) ? "is invalid" : "got hangup");
  1102.  
  1103.                 destroy_connection (global.clients[i]);
  1104.  
  1105.                 continue;
  1106.             }
  1107. #endif
  1108.             if (READABLE (global.clients[i]->fd))
  1109.             {
  1110.                 if (!global.clients[i]->destroy)
  1111.                     handle_connection (global.clients[i]);
  1112.             }
  1113.  
  1114.             if (WRITABLE (global.clients[i]->fd))
  1115.             {
  1116.                 if (global.clients[i]->connecting)
  1117.                     complete_connect (global.clients[i]);
  1118.  
  1119.                 else
  1120.                 {
  1121. #if HAVE_POLL
  1122.                     /* sanity check - make sure there was actually data to
  1123.                      * write.
  1124.                      */
  1125.                     if (!ISSERVER (global.clients[i])
  1126.                         && !global.clients[i]->sendbuf)
  1127.                     {
  1128.                         log ("main: ERROR, fd %d (id %d) was writable with no pending data", global.clients[i]->fd, global.clients[i]->id);
  1129.                         clear_write (global.clients[i]->fd);
  1130.                     }
  1131. #endif
  1132.  
  1133.                     if (send_queued_data (global.clients[i]) == -1)
  1134.                                 destroy_connection (global.clients[i]);
  1135.                 }
  1136.             }
  1137.  
  1138.             /* reap connections which have no logged in after
  1139.              * `Login_Timeout' seconds
  1140.              */
  1141.             if (ISUNKNOWN (global.clients[i]) &&
  1142.                 global.current_time - global.clients[i]->timer >
  1143.                 Login_Timeout)
  1144.             {
  1145.                 log ("main: terminating %s (login timeout)",
  1146.                      global.clients[i]->host);
  1147.                 send_cmd (global.clients[i], MSG_SERVER_ERROR,
  1148.                           "Idle timeout");
  1149.                 destroy_connection (global.clients[i]);
  1150.             }
  1151.         }
  1152.  
  1153.         /* handle timed-out remote searches */
  1154.         expire_searches ();
  1155.  
  1156. #ifndef ROUTING_ONLY
  1157.         /* check for stat server i/o */
  1158.         if (global.stat_server_fd != -1)
  1159.         {
  1160.             if (WRITABLE (global.stat_server_fd))
  1161.             {
  1162.                 int code;
  1163.                 socklen_t codesize = sizeof (code);
  1164.  
  1165.                 clear_write (global.stat_server_fd);
  1166.                 /* nonblocking connect complete - check connection code */
  1167.                 if (getsockopt (global.stat_server_fd, SOL_SOCKET, SO_ERROR,
  1168.                         &code, &codesize))
  1169.                 {
  1170.                     logerr ("main","getsockopt");
  1171. #if HAVE_POLL
  1172.                     remove_fd (global.stat_server_fd);
  1173. #endif
  1174.                 }
  1175.                 else if (code)
  1176.                 {
  1177.                     log ("main: connection to stat server failed (%s)",
  1178.                             strerror (code));
  1179. #if HAVE_POLL
  1180.                     remove_fd (global.stat_server_fd);
  1181. #endif
  1182.                 }
  1183.                 else
  1184.                     set_read (global.stat_server_fd);
  1185.             }
  1186.             else if (READABLE (global.stat_server_fd))
  1187.                 stat_server_read ();
  1188.         }
  1189.  
  1190.         /* check for stats port connections */
  1191.         if (sp != -1)
  1192.         {
  1193. #if HAVE_POLL
  1194.             if (global.poll[POFF (sp)].revents & POLLIN)
  1195. #else
  1196.             if (FD_ISSET (sp, &read_fds))
  1197. #endif /* HAVE_POLL */
  1198.                 report_stats (sp);
  1199.         }
  1200. #endif
  1201.  
  1202.         /* check for new clients */
  1203.         for (i = 0; i < sockfdcount; i++)
  1204.         {
  1205. #if HAVE_POLL
  1206.             if (global.poll[POFF (sockfd[i])].revents & POLLIN)
  1207. #else
  1208.             if (FD_ISSET (sockfd[i], &read_fds))
  1209. #endif /* HAVE_POLL */
  1210.                 accept_connection (sockfd[i]);
  1211.         }
  1212.  
  1213.         list_foreach (Servers, (list_callback_t) flush_server_data, 0);
  1214.  
  1215.         flood_expire ();
  1216.  
  1217.         /* execute any pending events now */
  1218.         exec_timers (global.current_time);
  1219.  
  1220.         /* remove destroyed connections from the client list.  this
  1221.          * MUST be the last operation in the loop since all the previous
  1222.          * can cause connections to be terminated.
  1223.          */
  1224.         reap_connections ();
  1225.     }
  1226.  
  1227.     /* close all open file descriptors properly */
  1228.     for (i = 0; i <= global.max_fd; i++)
  1229.         CLOSE (i);
  1230.  
  1231.     dump_state ();
  1232.  
  1233. #if DEBUG
  1234.  
  1235. #if HAVE_POLL
  1236.     FREE (global.poll);
  1237.     FREE (global.fdmap);
  1238. #endif
  1239.     for (i = 0; i < global.clients_num; i++)
  1240.     {
  1241.         global. clients[i]->fd = -1;
  1242.         remove_connection (global.clients[i]);
  1243.     }
  1244.     FREE (global.clients);
  1245.  
  1246.     FREE (sockfd);
  1247.  
  1248. #ifndef ROUTING_ONLY
  1249.     free_hash (Filter);
  1250.     free_hash (File_Table);
  1251. #endif
  1252.  
  1253.     free_hash (Users);
  1254.     free_hash (Channels);
  1255.     Channels = 0;
  1256.     free_hash (Hotlist);
  1257.     free_hash (User_Db);
  1258.     free_hash (Who_Was);
  1259.     free_hash (Clones);
  1260.     free_hash (Client_Versions);
  1261.     free_timers ();
  1262.  
  1263.     list_free (Bans, (list_destroy_t) free_ban);
  1264.     list_free (Server_Auth, (list_destroy_t) free_server_auth);
  1265.     list_free (Server_Names, free_pointer);
  1266.     list_free (Destroy, 0);
  1267.  
  1268.     /* free up memory associated with global configuration variables */
  1269.     free_config ();
  1270.     acl_destroy ();
  1271.  
  1272.     /* this displays a list of leaked memory.  pay attention to this. */
  1273.     CLEANUP ();
  1274. #endif
  1275.  
  1276. #if defined(WIN32) && !defined(__CYGWIN__)
  1277.     WSACleanup ();
  1278. #endif
  1279.  
  1280.     global. current_time = time (0);
  1281.     log ("main: server ended at %s", ctime (&global.current_time));
  1282.  
  1283.     exit (0);
  1284. }
  1285.